# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt


import os
import json

import frappe
from frappe import _


def setup_taxes_and_charges(company_name: str, country: str):
	if not frappe.db.exists('Company', company_name):
		frappe.throw(_('Company {} does not exist yet. Taxes setup aborted.').format(company_name))

	file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'country_wise_tax.json')
	with open(file_path, 'r') as json_file:
		tax_data = json.load(json_file)

	country_wise_tax = tax_data.get(country)

	if not country_wise_tax:
		return

	if 'chart_of_accounts' not in country_wise_tax:
		country_wise_tax = simple_to_detailed(country_wise_tax)

	from_detailed_data(company_name, country_wise_tax)
	update_regional_tax_settings(country, company_name)


def simple_to_detailed(templates):
	"""
	Convert a simple taxes object into a more detailed data structure.

	Example input:

	{
		"France VAT 20%": {
			"account_name": "VAT 20%",
			"tax_rate": 20,
			"default": 1
		},
		"France VAT 10%": {
			"account_name": "VAT 10%",
			"tax_rate": 10
		}
	}
	"""
	return {
		'chart_of_accounts': {
			'*': {
				'item_tax_templates': [{
					'title': title,
					'taxes': [{
						'tax_type': {
							'account_name': data.get('account_name'),
							'tax_rate': data.get('tax_rate')
						}
					}]
				} for title, data in templates.items()],
				'*': [{
					'title': title,
					'is_default': data.get('default', 0),
					'taxes': [{
						'account_head': {
							'account_name': data.get('account_name'),
							'tax_rate': data.get('tax_rate')
						}
					}]
				} for title, data in templates.items()]
			}
		}
	}


def from_detailed_data(company_name, data):
	"""Create Taxes and Charges Templates from detailed data."""
	coa_name = frappe.db.get_value('Company', company_name, 'chart_of_accounts')
	coa_data = data.get('chart_of_accounts', {})
	tax_templates = coa_data.get(coa_name) or coa_data.get('*', {})
	tax_categories = data.get('tax_categories')
	sales_tax_templates = tax_templates.get('sales_tax_templates') or tax_templates.get('*', {})
	purchase_tax_templates = tax_templates.get('purchase_tax_templates') or tax_templates.get('*', {})
	item_tax_templates = tax_templates.get('item_tax_templates') or tax_templates.get('*', {})

	if tax_categories:
		for tax_category in tax_categories:
			make_tax_category(tax_category)

	if sales_tax_templates:
		for template in sales_tax_templates:
			make_taxes_and_charges_template(company_name, 'Sales Taxes and Charges Template', template)

	if purchase_tax_templates:
		for template in purchase_tax_templates:
			make_taxes_and_charges_template(company_name, 'Purchase Taxes and Charges Template', template)

	if item_tax_templates:
		for template in item_tax_templates:
			make_item_tax_template(company_name, template)


def update_regional_tax_settings(country, company):
	path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
	if os.path.exists(path.encode("utf-8")):
		try:
			module_name = "erpnext.regional.{0}.setup.update_regional_tax_settings".format(frappe.scrub(country))
			frappe.get_attr(module_name)(country, company)
		except Exception as e:
			# Log error and ignore if failed to setup regional tax settings
			frappe.log_error()
			pass

def make_taxes_and_charges_template(company_name, doctype, template):
	template['company'] = company_name
	template['doctype'] = doctype

	if frappe.db.exists(doctype, {'title': template.get('title'), 'company': company_name}):
		return

	for tax_row in template.get('taxes'):
		account_data = tax_row.get('account_head')
		tax_row_defaults = {
			'category': 'Total',
			'charge_type': 'On Net Total',
			'cost_center': frappe.db.get_value('Company', company_name, 'cost_center')
		}

		if doctype == 'Purchase Taxes and Charges Template':
			tax_row_defaults['add_deduct_tax'] = 'Add'

		# if account_head is a dict, search or create the account and get it's name
		if isinstance(account_data, dict):
			tax_row_defaults['description'] = '{0} @ {1}'.format(account_data.get('account_name'), account_data.get('tax_rate'))
			tax_row_defaults['rate'] = account_data.get('tax_rate')
			account = get_or_create_account(company_name, account_data)
			tax_row['account_head'] = account.name

		# use the default value if nothing other is specified
		for fieldname, default_value in tax_row_defaults.items():
			if fieldname not in tax_row:
				tax_row[fieldname] = default_value

	doc = frappe.get_doc(template)

	# Data in country wise json is already pre validated, hence validations can be ignored
	# Ingone validations to make doctypes faster
	doc.flags.ignore_links = True
	doc.flags.ignore_validate = True
	doc.insert(ignore_permissions=True)
	return doc

def make_item_tax_template(company_name, template):
	"""Create an Item Tax Template.

	This requires a separate method because Item Tax Template is structured
	differently from Sales and Purchase Tax Templates.
	"""
	doctype = 'Item Tax Template'
	template['company'] = company_name
	template['doctype'] = doctype

	if frappe.db.exists(doctype, {'title': template.get('title'), 'company': company_name}):
		return

	for tax_row in template.get('taxes'):
		account_data = tax_row.get('tax_type')

		# if tax_type is a dict, search or create the account and get it's name
		if isinstance(account_data, dict):
			account = get_or_create_account(company_name, account_data)
			tax_row['tax_type'] = account.name
			if 'tax_rate' not in tax_row:
				tax_row['tax_rate'] = account_data.get('tax_rate')

	doc = frappe.get_doc(template)

	# Data in country wise json is already pre validated, hence validations can be ignored
	# Ingone validations to make doctypes faster
	doc.flags.ignore_links = True
	doc.flags.ignore_validate = True
	doc.insert(ignore_permissions=True)
	return doc

def get_or_create_account(company_name, account):
	"""
	Check if account already exists. If not, create it.
	Return a tax account or None.
	"""
	default_root_type = 'Liability'
	root_type = account.get('root_type', default_root_type)

	existing_accounts = frappe.get_all('Account',
		filters={
			'company': company_name,
			'root_type': root_type
		},
		or_filters={
			'account_name': account.get('account_name'),
			'account_number': account.get('account_number')
		})

	if existing_accounts:
		return frappe.get_doc('Account', existing_accounts[0].name)

	tax_group = get_or_create_tax_group(company_name, root_type)

	account['doctype'] = 'Account'
	account['company'] = company_name
	account['parent_account'] = tax_group
	account['report_type'] = 'Balance Sheet'
	account['account_type'] = 'Tax'
	account['root_type'] = root_type
	account['is_group'] = 0

	doc = frappe.get_doc(account)
	doc.flags.ignore_links = True
	doc.flags.ignore_validate = True
	doc.insert(ignore_permissions=True, ignore_mandatory=True)
	return doc

def get_or_create_tax_group(company_name, root_type):
	# Look for a group account of type 'Tax'
	tax_group_name = frappe.db.get_value('Account', {
		'is_group': 1,
		'root_type': root_type,
		'account_type': 'Tax',
		'company': company_name
	})

	if tax_group_name:
		return tax_group_name

	# Look for a group account named 'Duties and Taxes' or 'Tax Assets'
	account_name = _('Duties and Taxes') if root_type == 'Liability' else _('Tax Assets')
	tax_group_name = frappe.db.get_value('Account', {
		'is_group': 1,
		'root_type': root_type,
		'account_name': account_name,
		'company': company_name
	})

	if tax_group_name:
		return tax_group_name

	# Create a new group account named 'Duties and Taxes' or 'Tax Assets' just
	# below the root account
	root_account = frappe.get_all('Account', {
		'is_group': 1,
		'root_type': root_type,
		'company': company_name,
		'report_type': 'Balance Sheet',
		'parent_account': ('is', 'not set')
	}, limit=1)[0]

	tax_group_account = frappe.get_doc({
		'doctype': 'Account',
		'company': company_name,
		'is_group': 1,
		'report_type': 'Balance Sheet',
		'root_type': root_type,
		'account_type': 'Tax',
		'account_name': account_name,
		'parent_account': root_account.name
	})

	tax_group_account.flags.ignore_links = True
	tax_group_account.flags.ignore_validate = True
	tax_group_account.insert(ignore_permissions=True)

	tax_group_name = tax_group_account.name

	return tax_group_name


def make_tax_category(tax_category):
	doctype = 'Tax Category'
	if isinstance(tax_category, str):
		tax_category = {'title': tax_category}

	tax_category['doctype'] = doctype
	if not frappe.db.exists(doctype, tax_category['title']):
		doc = frappe.get_doc(tax_category)
		doc.insert(ignore_permissions=True)
